package com.gitee.wsl.api

import com.gitee.wsl.base.Tuple3
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.onFailure
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract


typealias Closeable = AutoCloseable

infix operator fun <A:Closeable,B:Closeable> A.plus(other:B) = Pair(this,other)

fun <A:Closeable,B:Closeable,R> Pair<A,B>.use(block: (A, B) -> R): R{
    first.use {
        second.use {
           return  block(first,second)
        }
    }
}

fun <A:Closeable,B:Closeable,C:Closeable,R> Tuple3<A,B,C>.use(block: (A, B, C) -> R): R{
    first.use {
        second.use {
            third.use {
                return  block(first,second,third)
            }
        }
    }
}

 @OptIn(ExperimentalContracts::class)
 inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    var exception: Throwable? = null
    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        this.closeFinally(exception)
    }
}

/** Execute [block] then close this. This will be closed even if [block] throws. */
/*
fun <T : Closeable?, R> T.autoClose(block: (T) -> R): R {
    var result: R? = null
    var thrown: Throwable? = null

    try {
        result = block(this)
    } catch (t: Throwable) {
        thrown = t
    } finally {
        try {
            this?.close()
        } catch (t: Throwable) {
            if (thrown == null) {
                thrown = t
            } else {
                thrown.addSuppressed(t)
            }
        }
    }

    if (thrown != null) throw thrown
    return result!!
}
*/


 inline fun <T : Closeable, R> T.closeOnError(block: (T) -> R): R {
    try {
        return block(this)
    } catch (e: Throwable) {
        close()
        throw e
    }
}

fun <E : Closeable> SendChannel<E>.safeTrySend(element: E) {
    trySend(element).onFailure { element.close() }
}

fun Closeable?.closeFinally(cause: Throwable?): Unit = when {
    this == null -> {}
    cause == null -> close()
    else ->
        try {
            close()
        } catch (closeException: Throwable) {
            cause.addSuppressed(closeException)
        }
}

fun Closeable.closeQuietly() {
    try {
        close()
    } catch (e: RuntimeException) {
        throw e
    } catch (_: Exception) {}
}